/**
 * Utility class for NFC communication
 */
class NFCUtil {
  /**
   * @param {NfcA} t NFC-A tag object.
   * @return {Promise<boolean>} Promise resolving to true if the data is sent successfully and legal data is received,
   *         otherwise resolves to false.
   */
  static async start(t) {
    try {
      const command = new Uint8Array([0x47, 0x11]);
      const response = await t.transceive(command);
      return response.length === 2 &&
             response[0] === 0x47 &&
             response[1] === 0x47;
    } catch (e) {
      return false;
    }
  }

  /**
   * @param {NfcA} t NFC-A tag object.
   * @return {Promise<number|null>} Promise resolving to the starting address. If an error occurs while obtaining
   *         the address or illegal data is received, resolves to null.
   */
  static async getStartAddress(t) {
    try {
      const command = new Uint8Array([0x60, 0x00, 0x00, 0x00, 0x00]);
      const response = await t.transceive(command);
      
      if (response.length === 6 &&
          response[0] === 0x60 &&
          response[5] === 0x60) {
        
        const addressBytes = response.slice(1, 5);
        // Use unsigned right shift to ensure unsigned result
        return ((addressBytes[0] << 24) >>> 0) |
               ((addressBytes[1] << 16) & 0xFF0000) |
               ((addressBytes[2] << 8) & 0xFF00) |
               (addressBytes[3] & 0xFF);
      } else {
        return null;
      }
    } catch (e) {
      return null;
    }
  }

  /**
   * @param {NfcA} t NFC-A tag object.
   * @param {number} startAddress The starting address for reading data.
   * @param {number} size The size of the data to be read (each data contains 4 bytes).
   * @return {Promise<Array<number>|null>} Promise resolving to the data read. If an error occurs during the reading process
   *         or illegal data is received, resolves to null.
   */
  static async read(t, startAddress, size) {
    const result = [];
    try {
      let currentAddress = startAddress;
      for (let i = 0; i < size; i++) {
        const command = new Uint8Array([
          0x80,
          ((currentAddress >> 24) & 0xFF),
          ((currentAddress >> 16) & 0xFF),
          ((currentAddress >> 8) & 0xFF),
          (currentAddress & 0xFF)
        ]);
        
        const response = await t.transceive(command);
        
        if (response.length === 6 &&
            response[0] === 0x80 &&
            response[5] === 0x80) {
          
          const dataBytes = response.slice(1, 5);
          // Use unsigned right shift to ensure unsigned result
          const value = ((dataBytes[0] << 24) >>> 0) |
                        ((dataBytes[1] << 16) & 0xFF0000) |
                        ((dataBytes[2] << 8) & 0xFF00) |
                        (dataBytes[3] & 0xFF);
          
          result.push(value);
          currentAddress += 4;
        } else {
          return null;
        }
      }
      return result;
    } catch (e) {
      return null;
    }
  }

  /**
   * @param {NfcA} t NFC-A tag object.
   * @param {number} startAddress The starting address for writing data.
   * @param {number} fnIdx The index at which the function is called.
   * @param {Array<number>} content The data content to be written.
   * @return {Promise<boolean>} Promise resolving to true if the data is written successfully, otherwise resolves to false
   *         if an error occurs during writing process or illegal data is received.
   */
  static async write(t, startAddress, fnIdx, content) {
    try {
      let currentAddress = startAddress;
      // Ensure fnIdx is treated as unsigned
      const fullContent = [(0x60000000 + fnIdx) >>> 0].concat(content);
      
      for (const data of fullContent) {
        const command = new Uint8Array([
          0x70,
          ((currentAddress >> 24) & 0xFF),
          ((currentAddress >> 16) & 0xFF),
          ((currentAddress >> 8) & 0xFF),
          (currentAddress & 0xFF),
          ((data >> 24) & 0xFF),
          ((data >> 16) & 0xFF),
          ((data >> 8) & 0xFF),
          (data & 0xFF)
        ]);
        
        const response = await t.transceive(command);
        
        if (!(response.length === 2 &&
              response[0] === 0x70 &&
              response[1] === 0x70)) {
          return false;
        }
        
        currentAddress += 4;
      }
      
      const completeCommand = new Uint8Array([0x60, 0x02, 0x00, 0x00, 0x00]);
      const completeResponse = await t.transceive(completeCommand);
      
      return completeResponse.length === 6 &&
             completeResponse[0] === 0x60 &&
             completeResponse[1] === 0x60 &&
             completeResponse[2] === 0x00 &&
             completeResponse[3] === 0x00 &&
             completeResponse[4] === fnIdx &&
             completeResponse[5] === 0x60;
    } catch (e) {
      return false;
    }
  }
}

module.exports = NFCUtil;



